Editing Shape Geometries
This programming recipe adds a new feature to the application created in the previous two recipes. With the code from this recipe, you can allow users to edit the geometric points of a previously created path, as shown in Figure 6-3.Figure 6-3 Editing a path geometry
When the user presses the mouse button, the code in this recipe determines if the mouse was pressed on a geometry control handle. If so, the code tracks the mouse movement, providing feedback as the user drags the mouse by constantly erasing and redrawing the path shape.
If the place where the user clicks the mouse isn't on a geometry control handle, the code in the recipe uses the code from the previous recipes to allow the user create a new path or select a previously existing path.
Overview of Recipe Steps
The steps in this recipe show you how to:
You need to follow all of the steps of this recipe to implement editing of
- Determine if the user is editing a path
- Determine whether the user pressed on a geometry control handle
- Provide feedback as the user drags the mouse
- Create path shapes for feedback
- Redraw path shapes as the user drags the mouse
- Edit the selected path shape to reflect the new geometric point
path shapes.Functions Used in This Recipe
QuickDraw GX functions used in this recipe:
GXHitTestShape
"Shape Objects"
QuickDraw GX ObjectsGXSetShapeHitTest
"Transform Objects"
QuickDraw GX ObjectsGXGetViewPortMouse
"QuickDraw GX and the
Macintosh Environment"
QuickDraw GX Environment and UtilitiesGXSetShapePoints
"Geometric Shapes"
QuickDraw GX GraphicsGXDrawShape
"Shape Objects"
QuickDraw GX ObjectsGXCopyToShape
"Shape Objects"
QuickDraw GX ObjectsGXDisposeShape
"Shape Objects"
QuickDraw GX ObjectsStandard Macintosh functions used in this recipe:
StillDown
"Event Manager"
Macintosh Toolbox EssentialsThis recipe gives a brief description of these functions; you can find complete reference information for these functions in the Inside Macintosh suite of books.
This recipe also uses a function from a QuickDraw GX library:
SetShapeFastXorTranser
transferMode library Recipe Step Descriptions
In this section, each step is described individually.
- Determine if the user is editing a path
In Step 1 of the previous recipe, you added new code to your
MyContentClick
function to determine if the user was selecting a path and, if so, to handle the path selection. In this recipe, you change the implementa-
tion of that function again--this time to determine if the user is pressing
on a geometry control handle. If so, you need to allow the user to drag
the control handle around and redraw the path shape to provide dragging feedback. First, you should add these lines of code to yourMyContentClick
sample function:
handled = MyHandleEditPath(&localPoint);
if (!handled)
handled = MyHandleSelectPath(&localPoint);
if (!handled)
MyHandleCreatePath(&localPoint);The
MyHandleEditPath
sample function determines whether a geometry control handle was hit and, if so, it provides feedback as the user drags
the mouse.Here is the flow of control for the
MyHandleEditPath
sample function:
boolean MyHandleEditPath(gxPoint *hitPoint)
{
int whichControl;
if (gCurrent->selectedPath != nil)
if (gCurrent->pointCount == 0)
if (MyUserPressedControl(hitPoint, &whichControl))
{
MyPathDrag(hitPoint, whichControl+1);
return(true);
}
return false;
}- Determine whether the user pressed on a geometry control handle
You can use the function
GXHitTestShape
to determine whether the user pressed the mouse button down on a geometry control handle. TheMyUserPressedControl
sample function calls theGXHitTestShape
function once for each geometry control handle until it determines that one of them was hit.
boolean MyUserPressedControl(gxPoint *hitPoint,
int *whichControl)
{
gxHitTestInfo result;
short index;
for (index = 0; index < kNumOfControls; index++) {
if (gCurrent->controls[index] != nil) {
GXHitTestShape(gCurrent->controls[index],
hitPoint, &result);
if ((result.what & gxBoundsPart) != 0) {
*whichControl = index;
return true;
}
}
}
return false;
}If the user hit the bounds part (that is, within the bounding rectangle) of one of the geometry control handles, this function returns
true
as the function result and returns the index (1 through 4) of the geometry control that
was hit.You can use the
GXSetShapeHitTest
function to indicate to QuickDraw GX that it should hit-test only against the bounds parts of the geometry control handles. To do this, you need to add this line of code to yourMyCreateControlHandle
function (defined on page 188):
GXSetShapeHitTest(*theControl, gxBoundsPart, ff(1));- Provide feedback as the user drags the mouse
If the user pressed the mouse button down on a geometry control handle, you want to allow him or her to edit the geometry of the path by dragging the mouse. The
MyPathDrag
sample function provides the appropriate feedback. Here is the flow of control for this function:
void MyPathDrag (gxPoint *originalPoint, int whichControl)
{
/* Declare local variables -- see Steps 4 and 5. */
MyErasePathControlHandles();
/* Create path shapes for feedback -- see Step 4. */
/* Prepare for feedback -- see step 5. */
do
/* Redraw paths as user drags mouse -- see Step 5. */
} while (StillDown());
/* Edit selected path -- see Step 6. */
}This function erases the geometry control handles, and then creates two path shapes to use for feedback. While the mouse is still down, this function edits the two feedback paths, and alternately draws and erases them to provide feedback while the user drags the mouse.
The next three steps--Steps 4 through 6--show how to implement the parts of this function.
- Create path shapes for feedback
The
MyPathDrag
function uses two path shapes to provide feedback as the user drags the mouse:
gxShape oldPath = nil;
gxShape newPath = nil;The
oldPath
path shape represents the previous position of the path and thenewPath
path shape represents the new position of the path as the user drags the mouse. Initially, both of these paths are copies of the path that the user selected:
oldPath = GXCopyToShape(nil, gCurrent->selectedPath);
SetShapeFastXorTransfer(oldPath &gBlack, &gWhite);
newPath = GXCopyToShape(nil, oldPath);Notice that the transfer mode of these paths is set using the
SetShapeFastXorTransfer
library function. This exclusive-OR transfer
mode makes it easy to erase and draw the paths fast, which is just what
you need when providing user feedback.- Redraw path shapes as the user drags the mouse
As the user drags the mouse, you need to erase the previous version of
the path, create a new version based on the new location of the mouse,
and draw the new version. To begin with, you need variables to store the previous and new positions of the mouse:
gxPoint oldPoint;
gxPoint newPoint;Initially, the previous position of the mouse is the same as the point the user originally pressed the mouse button on:
oldPoint.x = originalPoint->x;
oldPoint.y = originalPoint->y;You can use the following
do
loop to provide feedback as the user drags
the mouse:
do {
GXGetViewPortMouse(gCurrent->viewPort, &newPoint);
if (MyMouseMoved(newPoint, oldPoint)) {
GXSetShapePoints(newPath, /* shape to edit */
whichControl, /* which point */
1, /* how many points */
&newPoint); /* new point */
GXDrawShape(oldPath); /* erase old path */
GXDrawShape(newPath); /* draw new path */
GXCopyToShape(oldPath, newPath);
oldPoint = newPoint;
}
} while (StillDown());This
do
loop calls theGXGetViewPortMouse
function to determine the current position of the mouse in local (view port) coordinates and then calls theMyMouseMoved
function, which is defined as
boolean MyMouseMoved(gxPoint newPoint, gxPoint oldPoint) {
return ((newPoint.x != oldPoint.x) ||
(newPoint.y != oldPoint.y));
}If the user has moved the mouse since the last time through the loop, the code in the
do
loop needs to erase the old path and draw a new path reflecting the new position of the geometry point.The call to the
GXSetShapePoints
function edits the new path shape to reflect the new position of the mouse. This function replaces one of the geometric points in the new path shape. ThewhichControl
parameter specifies which geometric point is replaced--in this case, the geometric point corresponding to the geometry control handle that the user originally pressed the mouse on. ThenewPoint
parameter specifies the new value of the geometry point--in this case, the current position of the mouse.After editing the new path shape, the sample code erases the path in the previous position and draws the new path. Finally, the new path becomes the old path and the new point becomes the old point for the next pass through the
do
loop. When the user releases the mouse button, thedo
loop finishes executing and you can dispose of the path shapes used for feedback:
GXDisposeShape(oldPath);
GXDisposeShape(newPath);- Edit the selected path shape to reflect the new geometric point
Finally, your
MyPathDrag
function needs to do is edit the geometry of the real path--the one contained in the window picture--to reflect the new position of the geometry control handle.You can use the
GXSetShapePoints
function to edit the geometry of the
path shape:
GXSetShapePoints(gCurrent-selectedPath,
whichControl,
1,
&newPoint);At this point, you also need to dispose of the shape representing the old geometry control handle:
GXDisposeShape(gCurrent->controls[whichControl-1]);Now you can create a new geometry control handle at the new position:
MyCreateControlHandle(&gCurrent->controls[whichControl-1],
&newPoint);To update your window, you can call the
MyDrawWindow
function, which is defined on page 191.
Related Recipes
The previous two recipes, "Creating Geometric Shapes" on page 179 and "Selecting Shapes," on page 193 show how to allow the user to create and select path shapes. You must complete the steps of those recipes before using this recipe.The next recipe, "Editing Shape Transforms," shows how to provide a different kind of control handle--a transform control handle. These control handles allow the user to edit the transform of the shape, rather than the geometry of the shape.
The recipes in Chapter 4, "Using the QuickDraw GX Environment," show you how to initialize QuickDraw GX and set up the QuickDraw GX debugging facilities. You should read the recipes in that chapter before using any recipes in this chapter.
The recipes in Chapter 5, "Using Macintosh Windows," show you how to create Macintosh windows, attach QuickDraw GX view ports to them, and implement zooming, resizing, and scrolling. You need to be familiar with the information in that chapter before you can display QuickDraw GX graphics in a Macintosh window.
The recipes in Chapter 7, "Handling Typography," show you how to create and manipulate typographic, rather than graphics, shapes.
The recipes in Chapter 8, "Printing," show you how to send your graphics and typographic shapes to a printer.
Main | Page One | What's New | Apple Computer, Inc. | Find It | Contact Us | Help